#include "../include/pch.h"
#include "../include/commonConnectionPool.h"

ConnectionPool::ConnectionPool(){
    // 加载配置项
    if (!loadConfigFile())
    {
        return;
    }
    // 穿件初始数量的链接
    for (int i = 0; i < _initSize; ++i)
    {
        connection *p = new connection();
        p->connect(_ip,_port,_username,_password,_dbname);
        p->refreshAliveTime(); // 刷新一下队列的起始时间
        _connectionQue.push(p);
        _connectionCnt++;
    }
    
    // 启动一个新线程，作为连接的生产者
    thread produce (bind(&ConnectionPool::produceConnectionTask,this));
    produce.detach();

    // 启动一个新的定时线程，扫描超过maxIndexTime时间的空闲连接，进行对于的连接回收
    thread scanner (bind(&ConnectionPool::scannerConnectionTask,this));
    scanner.detach();
}
ConnectionPool* ConnectionPool::getConnectionPool(){
    static ConnectionPool pool;
    return &pool;
}
bool ConnectionPool::loadConfigFile()// 从配置文件中加载配置项
{
    FILE *pf = fopen("mysql.cnf","r");
    if (pf == nullptr)
    {
        LOG("mysql.cnf file is not exist!");
        return false;
    }
    while (!feof(pf)) // 如果文件指针非空
    {
        char line[1024] = {0};
        fgets(line,1024,pf);
        string str = line;
        int idx = str.find('=',0);
        if (idx == -1) // 没有等号  无效的配置项
        {
            continue;
        }

        int endidx = str.find('\n',idx); // 从idx的下标开始找
        string key = str.substr(0,idx);
        string value = str.substr(idx+1,endidx-1-idx);

        if (key == "ip")
        {
            _ip = value;
        }
        else if (key == "port")
        {
            _port = atoi(value.c_str());
        }
        else if (key == "username")
        {
            _username = value;
        }
        else if (key == "password")
        {
            _password = value;
        }
        else if (key == "initSize")
        {
            _initSize = atoi(value.c_str());
        }
        else if (key == "maxSize")
        {
            _maxSize = atoi(value.c_str());
        }
        else if (key == "maxIdleTime")
        {
            _maxIdleTime = atoi(value.c_str());
        }
        else if (key == "connectionTimeOut")
        {
            _connectionTimeOut = atoi(value.c_str());
        }
        else if (key == "dbname")
        {
            _dbname = value;
        }
    }
    
    return true;
}
// 运行在独立的线程中，专门负责生产新连接
void ConnectionPool::produceConnectionTask()
{
    for (;;)
    {
        unique_lock<mutex>lock(_queueMutex);
        while (!_connectionQue.empty())
        {
            cv.wait(lock); // 队列非空，此处生产线程进入等待状态

        }
        if (_connectionCnt < _maxSize)
        {
            // 连接上限没有到达上限，继续创建
            connection *p = new connection();
            p->connect(_ip,_port,_username,_password,_dbname);
            p->refreshAliveTime();
            _connectionQue.push(p);
            _connectionCnt++;
        }
        // 通知消费者线程可以消费连接
        cv.notify_all();
    }
}

// 给外部提供接口，从连接池中获取一个可用的空闲链接
shared_ptr<connection> ConnectionPool::getConnection(){
    unique_lock<mutex> lock(_queueMutex);
    while (_connectionQue.empty())
    {
        // sleep
        if (cv_status::timeout == cv.wait_for(lock,chrono::milliseconds(_connectionTimeOut))){
            // 等待队列的超时时间,此时代表是超时唤醒
            if (_connectionQue.empty())
            {
                LOG("获取空闲连接超时...获取连接失败！");
                return nullptr;
            }
        } 
    }
    // shared_ptr 智能指针析构，会把connection资源直接delete，相当于connection被close
    // 此时需要自定义shared_ptr的释放资源的方式，把connection归还到queue
    shared_ptr<connection> sp(_connectionQue.front(),
    [&](connection *pcon){
        // 这里在服务器线程中调用，要考虑队列的线程安全
        unique_lock<mutex> lock(_queueMutex);
        pcon->refreshAliveTime();
        _connectionQue.push(pcon);
    }
    );
    _connectionQue.pop();
    cv.notify_all();// 消费完了之后，通知生产者，检查队列是否为空，空则生产连接
    
    return sp;
}

// 启动一个新的定时线程，扫描超过maxIndexTime时间的空闲连接，进行对于的连接回收
void ConnectionPool::scannerConnectionTask(){
    for(;;){
        // 通过sleep模拟定时效果
        this_thread::sleep_for(chrono::seconds(_maxIdleTime));
        // 扫描整个队列释放多余连接
        unique_lock<mutex> lock(_queueMutex);

        while (_connectionCnt > _initSize)
        {
            connection *p = _connectionQue.front();
            if (p->getAliveTime() >= (_maxIdleTime * 10))
            {
                _connectionQue.pop();
                _connectionCnt--;
                delete p; // 调用~connection释放连接
            }
            else{
                break; // 对头的连接没有超过maxIdleTime，其他的连接肯定没有
            }
        }
    }
}